/********************************************
 * 功能说明: 简单封装Jackson转换类
 * 模块名称: 平台转换模块
 * 系统名称: java敏捷开发通用平台
 * 软件版权: 北京银杉科技技术有限公司
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2016年12月23日 下午9:15:41
 * 审核人员:
 * 相关文档:
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.cloudnote.common.jackson;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.deser.std.UUIDDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.databind.ser.std.UUIDSerializer;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.hyacinth.cloudnote.common.jackson.serializer.NullStringSerializer;
import com.hyacinth.cloudnote.common.jackson.serializer.StringEscapeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Date;
import java.util.TimeZone;
import java.util.UUID;

/**
 * 简单封装Jackson转换类
 * @author zhangfb
 * @version 1.0.0.1
 */
@Slf4j
public class JsonMapper extends ObjectMapper {

	private static final long serialVersionUID = -5716990259295690856L;

	private static JsonMapper mapper;
	
	public JsonMapper() {
		this(Include.NON_EMPTY);
	}
	
	public JsonMapper(Include include) {
		// 设置输出时包含属性的风格
		if (include != null) {
			this.setSerializationInclusion(include);
		}
		// 允许单引号、允许不带引号的字段名称
		this.configure(Feature.ALLOW_SINGLE_QUOTES, true);
		this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		/*
		 * 設定是否使用Enum的toString函數來讀寫Enum,
		 * 為False時時使用Enum的name()函數來讀寫Enum, 默認為False.
		 * 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用.
		 */
		this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
		this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
		// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
		this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		this.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
		this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
		this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
        // 空值处理为空串
		this.getSerializerProvider().setNullValueSerializer(NullStringSerializer.instance);
		// java时间模块
		this.registerModule(new JavaTimeModule());
		this.registerModule(new SimpleModule().addSerializer(Date.class, DateSerializer.instance));
		this.registerModule(new SimpleModule().addDeserializer(Date.class, DateDeserializers.DateDeserializer.instance));
		// UUID模块
		this.registerModule(new SimpleModule().addSerializer(UUID.class, new UUIDSerializer()));
		this.registerModule(new SimpleModule().addDeserializer(UUID.class, new UUIDDeserializer()));
		// Spring security支持
		// this.registerModule(new SimpleModule().addDeserializer(SimpleGrantedAuthority.class, SimpleGrantedAuthorityDeserializer.instance));
		// 进行HTML解码
		this.registerModule(new SimpleModule().addSerializer(String.class, StringEscapeSerializer.instance));
		// 设置时区getTimeZone("GMT+8:00")
		this.setTimeZone(TimeZone.getDefault());
	}
	
	/**
	 * 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用.
	 */
	public static JsonMapper getInstance() {
		if (mapper == null){
			mapper = new JsonMapper();
		}
		return mapper;
	}
	
	/**
	 * 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式，建议在内部接口中使用。
	 */
	public static JsonMapper nonDefaultMapper() {
		if (mapper == null){
			mapper = new JsonMapper(Include.NON_DEFAULT);
		}
		return mapper;
	}
	
	/**
	 * Object可以是POJO，也可以是Collection或数组。
	 * 如果对象为Null, 返回"null".
	 * 如果集合为空集合, 返回"[]".
	 */
	public String toJson(Object object) {
		try {
			return this.writeValueAsString(object);
		} catch (IOException e) {
			log.warn("write to json string error:" + object, e);
			return null;
		}
	}
	
	/**
	 * 反序列化POJO或简单Collection如List<String>.
	 * 
	 * 如果JSON字符串为Null或"null"字符串, 返回Null.
	 * 如果JSON字符串为"[]", 返回空集合.
	 * 
	 * 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)
	 */
	public <T> T fromJson(String jsonString, Class<T> clazz) {
		if (StringUtils.isBlank(jsonString)) {
			return null;
		}
		try {
			return this.readValue(jsonString, clazz);
		} catch (IOException e) {
			log.warn("parse json string error:" + jsonString, e);
			return null;
		}
	}
	
	/**
	 * 當JSON裡只含有Bean的部分屬性時，更新一個已存在Bean，只覆蓋該部分的屬性.
	 */
	public <T> T update(String jsonString, T object) {
		try {
			return this.readerForUpdating(object).readValue(jsonString);
		} catch (IOException e) {
			log.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
		}
		return null;
	}
	
	/**
	 * 輸出JSONP格式數據.
	 */
	public String toJsonP(String functionName, Object object) {
		return toJson(new JSONPObject(functionName, object));
	}

	/**
	 * 对象转换为JSON字符串
	 * @param object
	 * @return
	 */
	public static String toJsonString(Object object){
		return JsonMapper.getInstance().toJson(object);
	}

    /**
     * 对象转换为JSONP字符串
     *
     * @param object
     * @return
     */
    public static String toJsonPString(String functionName, Object object) {
        return JsonMapper.getInstance().toJsonP(functionName, object);
    }
	
	/**
	 * JSON字符串转换为对象
	 * @param jsonString
	 * @param clazz
	 * @return
	 */
	public static <T> T fromJsonString(String jsonString, Class<T> clazz){
		return JsonMapper.getInstance().fromJson(jsonString, clazz);
	}
}
